; -*- lexical-binding: t -*-
;;;; init-evil 全家桶配置 以及evil-leader 和 defalias 命令名

;;--------------------------------------------------------------------
;; evil https://github.com/emacs-evil/evil
(use-package evil
  ;; :defer 1
  :hook (after-init . evil-mode)
  :config
  (evil-mode 1)
                                        ;(setq evil-want-C-u-scroll t) ; c-u Scroll a half page up
  (setcdr evil-insert-state-map nil) ; 清除插入模式的快捷键, 回退到emacs模式
  ;;保留esc退出insert模式
  (define-key evil-insert-state-map [escape] 'evil-normal-state)
  ;; C-w 删除前一个词, 和终端保持一致
  (define-key evil-insert-state-map (kbd "C-w") 'backward-kill-word)
  ;; 进入dired-mode时, 采用emacs state
  (evil-set-initial-state 'dired-mode 'emacs)
  ;; 禁止undo-tree 在 mode-line 显示
  ;; (setq minor-mode-alist
  ;;     (remove '(undo-tree-mode undo-tree-mode-lighter)
  ;;             minor-mode-alist))
  (diminish 'undo-tree-mode)
  ;; 这里cl-lib这个包不是必需用，已在第一行用; -*- lexical-binding: t -*- 声明词法作用来域
  ;; 为了用 cl-incf，暂且放在这里require。 2022.3.26
  (require 'cl-lib)
  ;; Change the color of the mode-line depending on buffer state
  ;; (require cl) 
  ;; (lexical-let ....)
  ;; lexical-let 依赖 cl. cl包已不建议使用。建议用cl-lib。
  ;; https://www.emacswiki.org/emacs/Evil?msclkid=1a25d7c1ac5f11ec80a1a4297333dd2d#h5o-19
  (let ((default-color (cons (face-background 'mode-line)
                                     (face-foreground 'mode-line))))
               (add-hook 'post-command-hook
                         (lambda ()
                           (let ((color (cond ((minibufferp) default-color)
                                        ((evil-insert-state-p) '("#008080" . "#000000"))
                                              ((evil-emacs-state-p)  '("#444488" . "#ffffff"))
                                        ;((buffer-modified-p)   '("#006fa0" . "#ffffff"))
                                              (t default-color))))
                             (set-face-background 'mode-line (car color))
                             (set-face-foreground 'mode-line (cdr color)))))
               )
  )

;;---------------------------------------------------------------------



;;---------------------------------------------------------------------
;; evil-leader 配置, 使用general, 已废弃
;; (use-package evil-leader
;;    :after evil
;;    :init
;;   (global-evil-leader-mode)
;;    :config
;;                                         ;(setq evil-leader/in-all-states t)
;;   (evil-leader/set-leader "<SPC>")
;;   (evil-leader/set-key
;;            "ff" 'find-file
;;            "bb" 'switch-to-buffer
;;            "bk" 'kill-buffer
;;            "0"  'select-window-0
;;            "1"  'select-window-1
;;            "2"  'select-window-2
;;            "3"  'select-window-3
;;            "w/" 'split-window-right
;;            "w-" 'split-window-below
;;            "ww" 'delete-other-windows
;;            "uu" 'winner-undo
;;            "ur" 'winner-redo
;;            "xx"  'counsel-M-x
;;            "si"  'evilmi-select-items
;;            "av" 'avy-goto-char-2

;;            )
;;   )
;;-------------------------------------------------
;; undo-tree 操作
;; C-x u 进入 undo-tree-visualizer-mode
;; p n 上下移动，在分支之前 b f 左右切换
;; t 显示时间戳，选定需要的状态后， q 退出

;;   ;; evil mode coursor color base on mode
;;     (setq evil-default-cursor (quote (t "#750000"))
;;           evil-visual-state-cursor '("pink" box)
;;           evil-normal-state-cursor '("green" box)
;;           evil-insert-state-cursor '("yellow" bar)
;;           ;; Don’t move back the cursor one position when exiting insert mode
;;           evil-move-cursor-back nil))
(setq evil-want-fine-undo t)
;; 配置evil的undo-system
;; https://emacs-china.org/t/customize-evil-undo-system-for-redo-functionality/14969/3
;;(global-undo-tree-mode) ;  emacs 27 unvoid function
;;(evil-set-undo-system 'undo-tree)
;;(setq evil-undo-system 'undo-tree)      ;
;;---------------------------------------------------------------------
;; evil-escape
(use-package evil-escape
  :defer 3
  :after evil
  ;hook 使用 evil-mode-hook 不起作用
  ;:hook (evil-local-mode . evil-escape-mode )
  :init
  ;(evil-escape-mode 1)
  ;; evil-mode-hook 在启动后, 不起作用
  ;(add-hook 'evil-mode-hook (lambda ()(evil-escape-mode 1)))
  ;(add-hook 'emacs-startup-hook  (lambda ()(evil-escape-mode 1)))
  :config
  (evil-escape-mode)
  (diminish 'evil-escape-mode)
  ; "jj" 会按住j不放时, 引发escape
  (setq-default evil-escape-key-sequence "jk")
  (setq-default evil-escape-delay 0.2))
;;---------------------------------------------------------------------

;;---------------------------------------------------------------------
;; evil-matchit % 增强版 配对代码跳转
(use-package evil-matchit
  :commands (evilmi-jump-item ; SPC mm
             evilmi-select-items ; SPC si
             )
  :config
  (global-evil-matchit-mode 1)
  )
;;---------------------------------------------------------------------

;;---------------------------------------------------------------------
;; evil-surround
;; https://github.com/emacs-evil/evil-surround
(use-package evil-surround
  :defer 3
  :after evil
  :config
  (global-evil-surround-mode 1))

;; *Add surrounding*
;; You can surround in visual-state with S<textobject> or gS<textobject>.
;; Or in normal-state with ys<textobject> or yS<textobject>.
;; *Change surrounding*
;; You can change a surrounding with cs<old-textobject><new-textobject>.
;; *Delete surrounding*
;; You can delete a surrounding with ds<textobject>.
;; *yss 命令可以用于整行操作，忽略中间的空格,yS 和 ySS 还能让包围内容单独一行并且加上缩进。*
;; 包围符号为括号时，输入左括号 (或者{ ,则会留一个空格
;; 而右括号则不留空格，也是非常好用，看编码风格使用。
;; Suppose you want to call a function on your visual selection or a text object. You can simply press f instead of the aforementioned keys and are then prompted for a functionname in the minibuffer, like with the tags. So with:

;; (Hello world!)

;; … after selecting the string, then pressing Sf, entering print and pressing return you would get

;; print("Hello world!")
;; t 表示html tags
;; b 表示小括号 () B 表示花括号 {}
;;---------------------------------------------------------------------


;;---------------------------------------------------------------------
;; evil-embrace This package provides evil integration of
;; embrace.el. Since evil-surround provides a similar set of
;; features as embrace.el, this package aims at adding the goodies
;; of embrace.el to evil-surround and making evil-surround even
;; better.
;; https://github.com/cute-jumper/evil-embrace.el

;;---------------------------------------------------------------------



;;---------------------------------------------------------------------
;; 模拟vim的* #
;; https://github.com/bling/evil-visualstar
(use-package evil-visualstar
  :defer 3
  :after evil
  ;:hook (evil-local-mode . global-evil-visualstar-mode)
  )
;;---------------------------------------------------------------------

;;---------------------------------------------------------------------
;; evil-nerd-commenter 注释插件
;; https://github.com/redguardtoo/evil-nerd-commenter
(use-package evil-nerd-commenter
  :after evil
  ;; :defer 2
  :config
  (evilnc-default-hotkeys)
  )
;; (evil-leader/set-key
;; "ci" 'evilnc-comment-or-uncomment-lines
;;   "cl" 'evilnc-quick-comment-or-uncomment-to-the-line ;按行号注释或反注释, evil-repeat扩展区域
;;   "ll" 'evilnc-quick-comment-or-uncomment-to-the-line
;;   "cc" 'evilnc-copy-and-comment-lines
;;   "cp" 'evilnc-comment-or-uncomment-paragraphs
;;   "cr" 'comment-or-uncomment-region
;;   "cv" 'evilnc-toggle-invert-comment-line-by-line
;;   "."  'evilnc-copy-and-comment-operator
;;   "\\" 'evilnc-comment-operator ; if you prefer backslash key
;; )

;;---------------------------------------------------------------------

;;---------------------------------------------------------------------
;; smart-input-source
(use-package sis
  :ensure nil ; sis.el  存放在site-lisp
  :hook (evil-local-mode . sis-global-respect-mode)
  :config
  ; windows平台需要命令行程序im-select 1033 英文2052 中文
  ; lazyman-config语句应在第一行
  (if *is-windows* (sis-ism-lazyman-config "1033" "2052" 'im-select))
  (if *is-linux* (sis-ism-lazyman-config nil "pyim" 'native))
  (sis-global-respect-mode t)
  ;; enable the /context/ mode for all buffers
  (sis-global-context-mode t)
  (sis-global-inline-mode t)
  ;; (sis-global-follow-context-mode t)
  (sis-global-cursor-color-mode t)
  ;; Handle prefix key and buffer
  ;; (setq sis-respect-prefix-and-buffer nil)
  ;; Prefix keys to be respected default value: '("C-c" "C-x" "C-h")
  ;;  默认值会影响函数键绑定的显示, 虽然能正常执行
  (setq sis-prefix-override-keys (list "C-h"))
  )


;; (with-eval-after-load 'evil
;;   (require 'sis)
;;   (if *is-windows* (sis-ism-lazyman-config "1033" "2052" 'im-select))
;;   (sis-global-respect-mode t)
;;   (sis-global-inline-mode t)
;;   (sis-global-follow-context-mode t)
;;   (sis-global-cursor-color-mode t))

;---------------------------------------------------------------------

;;---------------------------------------------------------------------
;; 内置输入法 Linux 终端使用
(use-package pyim
  :bind ("C-\\" . toggle-input-method)
  :init
  (setq-default default-input-method "pyim")
  :config
  (pyim-basedict-enable)
  ;; 我使用全拼
  (setq pyim-default-scheme 'quanpin)
  ;; 选词框显示5个候选词
  (setq pyim-page-length 5)
  ;;使用半角标点
  (setq-default pyim-punctuation-translate-p '(no yes auto))
  )



;;基本词库设置
(use-package pyim-basedict
  :commands (pyim-basedict-enable))

;; 添加词库步骤
;; 运行 `pyim-dicts-manager' ，按照命令提示，将下载得到的词库文件信息
;; 添加到 `pyim-dicts' 中，最后运行命令 `pyim-restart' 或者重启 emacs，
;; 这个词库使用 `utf-8-unix' 编码。
;;--------------------------------------------------
;;https://blog.binchen.org/posts/how-to-input-non-english-character-in-evil-mode-efficiently.html
;; {{ make IME compatible with evil-mode
;; (defun evil-toggle-input-method ()
;;   "when toggle on input method, goto evil-insert-state. "
;;   (interactive)

;;   ;; load IME when needed, less memory footprint
;;   (unless (featurep 'pyim)
;;     (require 'pyim))

;;   (cond
;;    ((and (boundp 'evil-mode) evil-mode)
;;     ;; evil-mode
;;     (cond
;;      ((eq evil-state 'insert)
;;       (toggle-input-method))
;;      (t
;;       (evil-insert-state)
;;       (unless current-input-method
;;         (toggle-input-method))
;;       ))
;;     (if current-input-method (message "IME on!")))
;;    (t
;;     ;; NOT evil-mode, some guy don't use evil-mode at all
;;     (toggle-input-method))))

;; (defadvice evil-insert-state (around evil-insert-state-hack activate)
;;   ad-do-it
;;   (if current-input-method (message "IME on!")))

;; (global-set-key (kbd "C-\\") 'evil-toggle-input-method)
;; }}

;;---------------------------------------------------------------------
;; creat new evil text object

;; https://github.com/emacs-evil/evil-surround
;; Add new surround pairs through creation of evil objects

;; You can create new evil objects that will be respected by evil-surround. Just use the following code:

;; this macro was copied from here: https://stackoverflow.com/a/22418983/4921402
(defmacro define-and-bind-quoted-text-object (name key start-regex end-regex)
  (let ((inner-name (make-symbol (concat "evil-inner-" name)))
        (outer-name (make-symbol (concat "evil-a-" name))))
    `(progn
       (evil-define-text-object ,inner-name (count &optional beg end type)
         (evil-select-paren ,start-regex ,end-regex beg end type count nil))
       (evil-define-text-object ,outer-name (count &optional beg end type)
         (evil-select-paren ,start-regex ,end-regex beg end type count t))
       (define-key evil-inner-text-objects-map ,key #',inner-name)
       (define-key evil-outer-text-objects-map ,key #',outer-name))))

;; (define-and-bind-quoted-text-object "pipe" "|" "|" "|")
;; (define-and-bind-quoted-text-object "slash" "/" "/" "/")
;; (define-and-bind-quoted-text-object "asterisk" "*" "*" "*")

(with-eval-after-load 'tex
    ;; 增加新的文本对象 $$
    (define-and-bind-quoted-text-object "dollar" "$" "\\$" "\\$") ;; sometimes your have to escape the regex
    )


;;---------------------------------------------------------------------

;;---------------------------------------------------------------------
;;

;; https://github.com/emacs-evil/evil-surround
;; Add surround pairs for buffer-local text objects

;; Buffer-local text objects are useful for mode specific text objects that you don’t want polluting the global keymap. To make these objects work with evil-surround, do the following (for example to bind pipes to Q):

;; (defvar evil-some-local-inner-keymap (make-sparse-keymap)
;;   "Inner text object test keymap")
;; (defvar evil-some-local-outer-keymap (make-sparse-keymap)
;;   "Outer text object keymap")
;; (define-key evil-some-local-inner-keymap "Q" #'evil-inner-pipe)
;; (define-key evil-some-local-outer-keymap "Q" #'evil-a-pipe)
;; (define-key evil-visual-state-local-map   "iQ" #'evil-inner-pipe)
;; (define-key evil-operator-state-local-map "iQ" #'evil-inner-pipe)
;; (define-key evil-visual-state-local-map   "aQ" #'evil-a-pipe)
;; (define-key evil-operator-state-local-map "aQ" #'evil-a-pipe)
;; (setq evil-surround-local-inner-text-object-map-list (list evil-some-local-inner-keymap))
;; (setq evil-surround-local-outer-text-object-map-list (list evil-some-local-outer-keymap))
;; (setq-local evil-surround-pairs-alist (append '((?Q "|" . "|")) evil-surround-pairs-alist))

;; note that the binding to evil-some-local-(inner|outer)-keymap is purely for organizational perpouses, you can skip that step and do:

;; (define-key evil-visual-state-local-map   "iQ" #'evil-inner-pipe)
;; (define-key evil-operator-state-local-map "iQ" #'evil-inner-pipe)
;; (define-key evil-visual-state-local-map   "aQ" #'evil-a-pipe)
;; (define-key evil-operator-state-local-map "aQ" #'evil-a-pipe)
;; (setq evil-surround-local-inner-text-object-map-list (list (lookup-key evil-operator-state-local-map "i")))
;; (setq evil-surround-local-outer-text-object-map-list (list (lookup-key evil-operator-state-local-map "a")))
;; (setq-local evil-surround-pairs-alist (append '((?Q "|" . "|")) evil-surround-pairs-alist))

;; Add new supported operators

;; You can add support for new operators by adding them to evil-surround-operator-alist. For more information do: C-h v evil-surround-operator-alist.

;; By default, surround works with evil-change and evil-delete. To add support for the evil-paredit package, you need to add evil-paredit-change and evil-paredit-delete to evil-surround-operator-alist, like so:

;; (add-to-list 'evil-surround-operator-alist
;;              '(evil-paredit-change . change))
;; (add-to-list 'evil-surround-operator-alist
;;              '(evil-paredit-delete . delete))
;;---------------------------------------------------------------------


;;---------------------------------------------------------------------
;;
;;---------------------------------------------------------------------


;;---------------------------------------------------------------------
;; key bindings

;; 插入当前日期  函数定义在 kdl-functions.el
;; <f5> 也是windows notepad的快捷键
(global-set-key (kbd "<f5>") 'kdl-insert-current-time)

;;切换buffer
(global-set-key (kbd "<f7>") 'kdl-switch-to-prev-buffer)
(global-set-key (kbd "<f8>") 'kdl-switch-to-next-buffer)

;; 按v 进入 visual state, 再按v, 进行expand-region
;; 重复v, 扩大区域. 按 - 缩小区域按0 重置
(with-eval-after-load 'evil
  (define-key evil-visual-state-map (kbd "v") 'er/expand-region)
  )


;;---------------------------------------------------------------------



;;---------------------------------------------------------------------
;; general config
;; https://github.com/noctuid/general.el/

(use-package general
  :init
  (general-evil-setup t))

;; 代码借鉴陈斌
;;https://github.com/redguardtoo/emacs.d/blob/master/lisp/init-evil.el
;; {{ use `SPC` as leader key,` as leader key
(general-create-definer my-spc-leader-def
  :prefix "SPC"
  :states '(normal visual))

(my-spc-leader-def
  "0"  'select-window-0
  "1"  'select-window-1
  "2"  'select-window-2
  "3"  'select-window-3
  "av" 'avy-goto-char-2
  "bb" 'counsel-switch-buffer
  "bd" 'evil-delete-buffer
  "bf" 'kdl-new-empty-buffer
  "bo" 'counsel-switch-buffer-other-window
  "bm" 'counsel-bookmark
  "bl" 'bookmark-bmenu-list
  "bs" 'bookmark-set
  "ff" 'find-file
  "fr" 'counsel-recentf
  "im" 'counsel-imenu
  "ma" 'beginning-of-defun
  "md" 'mark-defun
  "me" 'end-of-defun
  "mm" 'evilmi-jump-items
  "pf" 'find-file-in-project
  "pi" 'pasteex-image
  "qr" 'query-replace-regexp
  "rr" 'query-replace-regexp
  "rs" 'isearch-forward-regexp
  "rg" 'counsel-rg
  "sb" 'swiper-isearch-backward
  "sc" 'shell-command
  "si" 'evilmi-select-items
  "ws" 'split-window-below
  "we" 'split-window-right
  "ur" 'winner-redo
  "uu" 'winner-undo
  "ww" 'delete-other-windows
  "wd" 'delete-window
  ;; 模拟vim xp, 交换光标下和后面两个字母位置
  "xp" '(lambda () (interactive) (evil-delete (point) (1+ (point))) (evil-paste-after 1))
  "xx"  'counsel-M-x
  "yi" 'yas/insert-snippet
  "yn" 'yas/new-snippet
  "ye" 'yas/expand
  ;; 复制整行内容, 但不包括换行
  "yy" '(lambda () (interactive)
          (kill-ring-save (line-beginning-position)
                          (line-end-position))
          (message "copied whole line! exclued <CR>"))
  ";" 'evil-repeat-find-char-reverse
  )

;;---------------------------------------------------------------------

;; comma leader key 少量普通命令, 应主要用于特殊模式命令
(general-create-definer my-comma-leader-def
  :prefix ","
  :states '(normal visual)
  )

(my-comma-leader-def
  "cl" 'evilnc-quick-comment-or-uncomment-to-the-line
  "ci" 'evilnc-comment-or-uncomment-lines
  "cc" 'evilnc-copy-and-comment-lines
  ;; 以下自定义函数
  "da" 'kdl-increment-number-decimal ;maintains field sizes
  "aa" 'kdl-increment-number-at-point ; vim C-a
  "dx" 'kdl-decrement-number-decimal ;maintains field sizes
  "xx" 'kdl-decrement-number-at-point ; vim C-x
  )

;;---------------------------------------------------------------------
(general-create-definer my-markdown-leader-der
  :prefix ","
  :non-normal-prefix  "C-,"
  :states '(normal insert visual emacs)
  :keymaps 'markdown-mode-map
  )

(my-markdown-leader-der
  "<tab>" 'markdown-cycle
  "ic" 'markdown-insert-gfm-code-block ; C-g 取消选择语言
  "bb" 'markdown-insert-bold
  "bq" 'markdown-blockquote-region
  "tg" 'markdown-toc-generate-toc
  "tr" 'markdown-toc-refresh-toc
  "td" 'markdown-toc-delete-toc
  )


;;---------------------------------------------------------------------
;; per-major-mode setup

(general-create-definer my-latex-leader-def
  :prefix ","
  :non-normal-prefix  "C-,"
  :states '(normal insert visual emacs)
  :keymaps 'LaTeX-mode-map
  )
;; M-, default runs the command xref-pop-marker-stack

(my-latex-leader-def
 "pp" 'preview-at-point
 "pe" 'preview-environment
 "ps" 'preview-section
 "pr" 'preview-region
 "pb" 'preview-buffer
 "pd" 'preview-document
 "pcp" 'preview-clearout-at-point
 "pcs" 'preview-clearout-section
 ;; outline-minor-mode
 "sa" 'outline-show-all
 "se" 'outline-show-entry
 "hb" 'outline-hide-body
 "he" 'outline-hide-entry
 ;; reftex
 "rl" 'reftex-lable
 "rr" 'reftex-reference
 ;;CDLaTeX
 "cq" '(lambda ()(interactive)(kill-buffer " *CDLaTeX Help*")(delete-other-windows))
 )


;; (general-create-definer my-javascript-leader-def
;;   :prefix ","
;;   :non-normal-prefix "M-SPC"
;;   :states '(normal motion insert emacs)
;; :keymaps 'js2-mode-map)

;; Using a different prefix for the
;; insert and emacs states (or any state in
;; general-non-normal-states) can be done with :non-normal-prefix or
;; :global-prefix. By default, :prefix will apply to all keys, but
;; if one (or both) of the other prefix keywords is specified,
;; :prefix will only apply to evil states not listed in
;; general-non-normal-states. This is also the case for the global
;; evil keymaps such as evil-normal-state-map. :non-normal-prefix
;; will always only apply to the non-normal states. :global-prefix
;; will always apply to all keys. For example, this command will
;; bind SPC / to swiper in normal state and M-SPC / to swiper in
;; emacs and insert state:

;; (my-javascript-leader-def
;;   "de" 'js2-display-error-list
;;   "nn" 'js2-next-error
;;   "te" 'js2-mode-toggle-element
;;   "tf" 'js2-mode-toggle-hide-functions
;;   "jeo" 'js2r-expand-object
;;   "jco" 'js2r-contract-object
;;   "jeu" 'js2r-expand-function
;;   "jcu" 'js2r-contract-function
;;   "jea" 'js2r-expand-array
;;   "jca" 'js2r-contract-array
;;   "jwi" 'js2r-wrap-buffer-in-iife
;;   "jig" 'js2r-inject-global-in-iife
;;   "jev" 'js2r-extract-var
;;   "jiv" 'js2r-inline-var
;;   "jrv" 'js2r-rename-var
;;   "jvt" 'js2r-var-to-this
;;   "jag" 'js2r-add-to-globals-annotation
;;   "jsv" 'js2r-split-var-declaration
;;   "jss" 'js2r-split-string
;;   "jef" 'js2r-extract-function
;;   "jem" 'js2r-extract-method
;;   "jip" 'js2r-introduce-parameter
;;   "jlp" 'js2r-localize-parameter
;;   "jtf" 'js2r-toggle-function-expression-and-declaration
;;   "jao" 'js2r-arguments-to-object
;;   "juw" 'js2r-unwrap
;;   "jwl" 'js2r-wrap-in-for-loop
;;   "j3i" 'js2r-ternary-to-if
;;   "jlt" 'js2r-log-this
;;   "jsl" 'js2r-forward-slurp
;;   "jba" 'js2r-forward-barf
;;   "jk" 'js2r-kill)
;; }}

;;---------------------------------------------------------------------

;;---------------------------------------------------------------------
;; defalias 命令别名
(defalias 'af 'auto-fill-mode)
(defalias 'bd 'evil-delete-buffer)
(defalias 'bb 'counsel-switch-buffer)
;; 对于MS Office word, Alt-x 快捷键可在字符和unicode编码之间切换
(defalias 'cd 'describe-char) ; ga , C-x = 'what-cursor-position
(defalias 'ci 'insert-char) ; 以字符unicode编码, 输入字符
(defalias 'yi 'yas-insert-snippet) ; 选中文本后, 调用一个warp 模板
(defalias 'ii 'iimage-mode) ;开启图片模式

;;---------------------------------------------------------------------

(provide 'init-evil)
